package com.ogier.app.jeu;

import javax.swing.event.EventListenerList;

import com.ogier.app.jeu.pieces.Piece;
import com.ogier.app.jeu.pieces.Roi;

/**
 * @author gogier
 *
 */
/**
 * @author gogier
 * 
 */
/**
 * @author gogier
 *
 */
/**
 * @author gogier
 *
 */
/**
 * @author gogier
 * 
 */
public class Plateau {

	public final static int			ETAT_OK			= 0;
	public final static int			ETAT_EN_ECHEC	= 1;
	public final static int			ETAT_ECHEC_MAT	= 2;
	public final static int			ETAT_ECHEC_PAT	= 3;
	public final static int			ETAT_KO_ERREUR	= -1;

	protected Case[][]				plateau;
	protected int					joueurActuel;
	protected Joueur[]				joueurs;

	protected Deplacement			deplacementEnCours;

	protected HistoriqueDeplacement	historiqueDeplacements;

	private int						nombreDeCoups;

	/* Gestion des évènements */
	/* Les évènements :
		 - pieces prises
		 - historiqueDeplacements
		 - joueurActuel */
	private final EventListenerList	listeners		= new EventListenerList();

	public Plateau() {
		super();

		// création du plateau
		plateau = new Case[8][8];

		joueurs = new Joueur[2];
		joueurs[Piece.BLANC] = new Joueur(Piece.BLANC, this);
		joueurs[Piece.NOIR] = new Joueur(Piece.NOIR, this);

		joueurActuel = Piece.BLANC;

		historiqueDeplacements = new HistoriqueDeplacement();

		nombreDeCoups = 0;

		initialisationPlateau();

		for (Joueur j : joueurs) {
			j.initialiserPiecesSurPlateau();
		}

	}

	/**
	 * ====================
	 * 
	 * INITIALISATION
	 * 
	 * ====================
	 */

	public void initialisationPlateau() {

		// Initialisation
		for (int i = 0; i < 8; i++) {
			for (int j = 0; j < 8; j++) {

				plateau[i][j] = new Case(i, j, this);

			}
		}

	}

	public int getIndexPremiereLigne(int couleur) {
		if (couleur == Piece.BLANC)
			return 6;
		else
			return 1;
	}

	public int getIndexSecondeLigne(int couleur) {
		if (couleur == Piece.BLANC)
			return 7;
		else
			return 0;
	}

	/**
	 * ====================
	 * 
	 * JEU
	 * 
	 * ====================
	 */

	/**
	 * @param listener
	 */
	public void addPlateauListener(PlateauListener listener) {
		listeners.add(PlateauListener.class, listener);
	}

	/**
	 * @param listener
	 */
	public void removePlateauListener(PlateauListener listener) {
		listeners.remove(PlateauListener.class, listener);
	}

	/**
	 * Passe le tour au joueur suivant
	 * 
	 * @return
	 */
	private Joueur joueurSuivant() {
		if (joueurActuel == Piece.BLANC) {
			joueurActuel = Piece.NOIR;
		} else {
			joueurActuel = Piece.BLANC;
		}
		nombreDeCoups++;
		fireJoueurActuelChanged();
		return joueurs[joueurActuel];
	}

	/**
	 * 
	 * Récupère la case se trouvant à une certaine position par rapport à la
	 * position actuelle
	 * 
	 * Sens identique pour les blancs et les noirs : toujours vers l'avant et la
	 * droite.
	 * 
	 * @param caseActuelle
	 * @param diffPosX
	 * @param diffPosY
	 * @return
	 */
	public Case getCase(Case caseActuelle, int diffPosX, int diffPosY) {
		Case resultat = null;
		// Vérification du joueur
		int nX;
		int nY;
		if (joueurActuel == Piece.BLANC) {
			// position de départ j=6&7
			nX = caseActuelle.getPositionX() - diffPosX;
			nY = caseActuelle.getPositionY() + diffPosY;
			if ((nX >= 0) && (nX < 8) && (nY >= 0) && (nY < 8))
				resultat = plateau[nX][nY];
		} else {
			nX = caseActuelle.getPositionX() + diffPosX;
			nY = caseActuelle.getPositionY() + diffPosY;
			if ((nX >= 0) && (nX < 8) && (nY >= 0) && (nY < 8))
				resultat = plateau[nX][nY];
		}

		return resultat;
	}

	/**
	 * Retourne la case à la position précisée sur le tableau
	 * 
	 * // Description du tableau // - 0 1 2 3 4 5 6 7 // 0 // 1 // 2 // 3 // 4
	 * // 5 // 6 // 7
	 * 
	 * @param i
	 * @param j
	 * @return
	 */
	public Case getCase(int i, int j) {
		if ((i >= 0) && (i < 8) && (j >= 0) && (j < 8))
			return plateau[i][j];
		else
			return null;
	}

	public Case getCase(String position) {
		if (position.length() == 2) {

			int i = 8 - Integer.parseInt(position.substring(1));
			int j = position.charAt(0) - 97;
			return plateau[i][j];
		}
		return null;
	}

	/**
	 * 
	 * Déplace une pièce d'une case à une autre si la case de destination
	 * contient une piece adverse cell-ci est supprimé du plateau et déplacé
	 * dans la liste des pieces prises
	 * 
	 * @param p
	 * @param caseDepart
	 * @param caseArrivee
	 * @return
	 */
	public boolean deplacerPiece(Piece p, Case caseDepart, Case caseArrivee) {
		boolean resultat = false;
		if (caseArrivee.getPiece() != null) {
			// il y a une piece sur la case
			if (p.getCouleur() != caseArrivee.getPiece().getCouleur()) {
				// La piece est une autre couleur
				caseDepart.setPiece(null);
				Piece piecePrise = caseArrivee.setPiece(p);
				ajouterPiecePrise(piecePrise);

				deplacementEnCours = new Deplacement(p, caseDepart, caseArrivee, piecePrise);
				resultat = true;
			} else {
				// Impossible de prendre une piece de la m�me couleur
				resultat = false;
			}
		} else {
			// il n'y a pas de piece
			caseDepart.setPiece(null);
			caseArrivee.setPiece(p);

			resultat = true;

			int roc = -1;
			// On vérifie s'il s'agit d'un roc
			if (p.getType() == Piece.PIECE_ROI) {

				Roi r = (Roi) p;
				// Si le déplacement sur les Y est supérieur à une case
				// alors c'est qu'il s'agit d'un roc
				int depY = caseArrivee.getPositionY() - caseDepart.getPositionY();
				if (depY > 1) {
					// On déplace alors la tour2 (à droite du plateau)
					resultat = deplacerPiece(r.getTour2(), getCase(caseArrivee, 0, -1));
					roc = Roi.ROC_PETIT;

				} else {
					if (depY < -1) {
						// on déplace alors la tour1 (à gauche du plateau)
						resultat = deplacerPiece(r.getTour1(), getCase(caseArrivee, 0, 1));
						roc = Roi.ROC_GRAND;
					}
				}
			}
			if (roc >= 0) {
				deplacementEnCours = new Deplacement(p, caseDepart, caseArrivee, roc);
			} else {
				deplacementEnCours = new Deplacement(p, caseDepart, caseArrivee);
			}

		}

		return resultat;
	}

	/**
	 * 
	 * Déplacer une piece de sa position vers une autre
	 * 
	 * @param p
	 * @param caseArrivee
	 * @return
	 */
	public boolean deplacerPiece(Piece p, Case caseArrivee) {
		boolean resultat = false;
		Case caseDepart = p.getPosition();

		return deplacerPiece(p, caseDepart, caseArrivee);
	}

	/**
	 * 
	 * Faire Déplacer une piece de sa position vers une autre par le joueur
	 * actuel
	 * 
	 * @param p
	 * @param caseArrivee
	 * @return
	 */
	public boolean deplacerPieceParJoueurActuel(Piece p, Case caseArrivee) {
		return getJoueurActuel().deplacerPiece(p, caseArrivee);
	}

	/**
	 * Permet de faire un retour arrière sur le dernier mouvement
	 */
	public void retourArriereDeplacement() {

		if (deplacementEnCours.piecePrise != null) {
			retourArrierePiecePrise(deplacementEnCours.piecePrise, deplacementEnCours.caseArrivee);
		} else {
			deplacementEnCours.caseArrivee.setPiece(null);
		}

		deplacementEnCours.caseDepart.setPiece(deplacementEnCours.pieceEnDeplacement);

		deplacementEnCours = null;
	}

	/**
	 * Permet de valider le coup joué
	 * 
	 * @return true si reste des coups à jouer, false si jeu terminé :
	 *         vérification Mat Ou Pat avec isMat ou isPat
	 */
	public int valider() {
		int value = ETAT_OK;
		// Au moment de la validation

		// On vérifie si un mouvement a été effectué
		if (deplacementEnCours != null) {

			// S'il y a eu un déplacement c'est qu'il était valide

			// donc le joueur actuel n'est plus en échec
			getJoueurActuel().getRoi().setEnEchec(false);

			// On réalise des vérifications
			// 1 - si le roi adverse est en échec
			boolean echec = getJoueurEnAttente().getRoi().checkIfEchec();
			// Si le joueur adverse est en echec alors on le tag comme en echec
			if (echec) {
				getJoueurEnAttente().getRoi().setEnEchec(true);
				deplacementEnCours.setEchec();
			}

			// 2 - si le joueur suivant peut jouer
			boolean jeuPossible = true;// getJoueurEnAttente().checkDeplacementsDisponibles();

			if (!jeuPossible) {
				// Le joueur ne peut plus jouer.
				// S'il est en echec alors le joueur actuel à gagner
				if (echec) {
					value = ETAT_ECHEC_MAT;
					deplacementEnCours.setMat();
					//
				} else {
					// sinon c'est PAT
					value = ETAT_ECHEC_PAT;
					deplacementEnCours.setPat();
				}
			} else {
				// il reste du jeu
				// On passe au joueur suivant*
				deplacementEnCours.pieceEnDeplacement.validerDeplacement();

				// historisation du mouvement
				historiqueDeplacements.add(deplacementEnCours);
				fireHistoriqueDeplacementChanged();

				deplacementEnCours = null;
				joueurSuivant();
			}
		} else {

			// Aucun déplacement valide n'a été réalisé
			value = ETAT_KO_ERREUR;
		}

		return value;
	}

	/**
	 * Permet d'annuler le coup joué
	 */
	public void annuler() {
		if (deplacementEnCours != null) {
			retourArriereDeplacement();
		}
	}

	/**
	 * ====================
	 * 
	 * FONCTIONS INTERNES
	 * 
	 * ====================
	 */
	/**
	 * Permet d'effectuer un retour arrière d'une pièce sur la case d'origine
	 * 
	 * @param laPiecePrise
	 * @param caseSource
	 */
	private void retourArrierePiecePrise(Piece laPiecePrise, Case caseSource) {
		joueurs[autreCouleur(laPiecePrise.getCouleur())].getPrisesPiecesAdverses().remove(laPiecePrise);
		joueurs[laPiecePrise.getCouleur()].getPiecesSurLePlateau().add(laPiecePrise);

		caseSource.setPiece(laPiecePrise);

		firePiecesPrisesChanged();
	}

	/**
	 * ajoute une piece dans les listes des pieces prises
	 * 
	 * @param laPiecePrise
	 */
	private void ajouterPiecePrise(Piece laPiecePrise) {
		joueurs[laPiecePrise.getCouleur()].getPiecesSurLePlateau().remove(laPiecePrise);
		joueurs[autreCouleur(laPiecePrise.getCouleur())].getPrisesPiecesAdverses().add(laPiecePrise);

		laPiecePrise.setPosition(null);

		firePiecesPrisesChanged();
	}

	/**
	 * @param couleur
	 * @return
	 */
	private int autreCouleur(int couleur) {
		return ((couleur + 1) % 2);
	}

	/**
	 * Génération de l'évènement sur modification de l'historique de déplacement
	 */
	protected void fireHistoriqueDeplacementChanged() {
		for (PlateauListener listener : listeners.getListeners(PlateauListener.class)) {
			listener.historiqueDeplacementChanged();
		}
	}

	/**
	 * Génération de l'évènement sur modification des pièces prises
	 */
	protected void firePiecesPrisesChanged() {
		for (PlateauListener listener : listeners.getListeners(PlateauListener.class)) {
			listener.piecesPrisesChanged();
		}
	}

	/**
	 * Génération de l'évènement sur modification du joueur actuel
	 */
	protected void fireJoueurActuelChanged() {
		for (PlateauListener listener : listeners.getListeners(PlateauListener.class)) {
			listener.joueurActuelChanged();
		}
	}

	/**
	 * ====================
	 * 
	 * GETTER/SETTER
	 * 
	 * ====================
	 */

	/**
	 * @return
	 */
	public Joueur getJoueurActuel() {
		return joueurs[joueurActuel];
	}

	/**
	 * @param couleur
	 * @return
	 */
	public Joueur getJoueur(int couleur) {
		return joueurs[couleur];
	}

	/**
	 * @return
	 */
	public Joueur getJoueurEnAttente() {
		return joueurs[autreCouleur(joueurActuel)];
	}

	/**
	 * @return
	 */
	public int getCouleurJoueurActuel() {
		return joueurActuel;
	}

	/**
	 * @return
	 */
	public int getCouleurJoueurEnAttente() {
		return getJoueurEnAttente().getCouleur();
	}

	/**
	 * @return
	 */
	public String getJoueurActuelToString() {
		String res;
		if (joueurActuel == Piece.BLANC) {
			res = "w";
		} else {
			res = "b";
		}
		return res;
	}

	/**
	 * @param couleur
	 * @return
	 */
	public boolean estEnEchec(int couleur) {
		return joueurs[couleur].estEnEchec();
	}

	/**
	 * @param couleur
	 * @return
	 */
	public Roi getRoi(int couleur) {
		return joueurs[couleur].getRoi();
	}

	/**
	 * @return
	 */
	public String getRockPossibleToString() {
		return "";
	}

	/**
	 * @return
	 */
	public String getCasePriseEnPassantPossibleToString() {
		return "";
	}

	/**
	 * @return
	 */
	public int getNbDemiCoups() {
		return 0;
	}

	/**
	 * @return
	 */
	public int getNbCoups() {
		return nombreDeCoups;
	}

	/**
	 * @return
	 */
	public Deplacement getDeplacementEnCours() {
		return deplacementEnCours;
	}

	/**
	 * @param deplacementEnCours
	 */
	public void setDeplacementEnCours(Deplacement deplacementEnCours) {
		this.deplacementEnCours = deplacementEnCours;
	}

	/**
	 * @return the historiqueDeplacements
	 */
	public HistoriqueDeplacement getHistoriqueDeplacements() {
		return historiqueDeplacements;
	}

	/**
	 * @param historiqueDeplacements the historiqueDeplacements to set
	 */
	public void setHistoriqueDeplacements(HistoriqueDeplacement historiqueDeplacements) {
		this.historiqueDeplacements = historiqueDeplacements;
	}

	/*
	* (non-Javadoc)
	* 
	* @see java.lang.Object#toString()
	*/
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();

		builder.append("\n |0|1|2|3|4|5|6|7|\n");

		for (int i = 0; i < 8; i++) {
			builder.append(i).append("|");
			for (int j = 0; j < 8; j++) {
				if (plateau[i][j].getPiece() != null) {
					builder.append(plateau[i][j].getPiece().toString()).append("|");
				} else {
					builder.append(" |");
				}

			}
			builder.append("\n");
		}

		builder.append("------------------");

		return builder.toString();
	}
}
